'''
Created on 29-01-2012

@author: Michal Golinski, Kamil Litwiniuk
'''
from SimPy.Simulation import *
from collections import deque
from Host import OutputMonitor

OUTPUT_SPEED = 1000000

class Router(Process):

    
    
    def __init__(self, identifier):
        Process.__init__(self, name='Router' + str(identifier))
        
        self.identifier = identifier
        self.outputPorts = []
        self.receiveEvent = SimEvent(name='Message receive event')

    def receiveMessage(self, finishTime):
        while now() < finishTime:
            yield waitevent, self, self.receiveEvent
             
            message = self.receiveEvent.signalparam
            #print "Received message by ROUTER", self.identifier, message.description()
            
            targetPort = self.getPortIndex(message)      
            targetPort.sheduler.enqueueMessage(message)  
            
    def getPortIndex(self, message):
        
        nodeList = message.path.split('-')
        try:
            currentNodeIndex = nodeList.index(self.identifier)
        except ValueError:
            print "Exception nodeList", nodeList, message, self.identifier
        
        for port in self.outputPorts:
            if port.target != "end":
                if port.target.identifier == nodeList[currentNodeIndex+1]:
                    return port
            elif port.target == "end" and port.target == nodeList[currentNodeIndex+1]:
                return port
        
        raise Exception('Wrong path or node identifier definition ' + message.path )
            
           
    def activatePorts(self, finishTime):
        for port in self.outputPorts:
            activate(port, port.sendMessages(finishTime))
        
    def __repr__(self):
        return "ROUTER " + self.identifier
    
    def __str__(self):
        return "ROUTER " + self.identifier
            
class OutputPort(Process):
      
    def __init__(self, parentRouter, target, sheduler):
        Process.__init__(self, name='OutputPort ' + ' R_' + str(parentRouter.identifier))
        
        self.parentRouter = parentRouter
        self.target = target
        self.sheduler = sheduler
        
        self.outputMonitor = OutputMonitor()
    
    def sendMessages(self, finishTime):
        while now() < finishTime:
            msgsToSend = self.sheduler.dequeueMessage()    
            if len(msgsToSend) != 0:
                sizeSum = 0
                for msgToSend in msgsToSend:
                    #print "Sending", msgToSend.description(), " from ROUTER", self.parentRouter.identifier, "to", self.target, "at", now(), "Stop sending for", float(msgToSend.size)/OUTPUT_SPEED
                    self.outputMonitor.monitorMessage(msgToSend)
                    
                    if (isinstance(self.target, str)):
                        print "END NODE, target", self.target, "at", now(), "message", msgToSend
                    else:
                        self.target.receiveEvent.signal(msgToSend)
                    
                    sizeSum += msgToSend.size
            
                yield hold, self, float(sizeSum)/OUTPUT_SPEED
            elif self.sheduler.isEmpty == True:
                yield waitevent, self, self.parentRouter.receiveEvent

        
class FcfsScheduler:
    
    def __init__(self):
        self.outputBuffer = deque([])
        self.isEmpty = True
        self.name = "FCFS"
        
    def enqueueMessage(self, message):
        self.isEmpty = False
        self.outputBuffer.append(message)
        
    def dequeueMessage(self):
        if (len(self.outputBuffer) > 0):
            return [self.outputBuffer.popleft()]
        else:
            self.isEmpty = True
            return []
        
class DrrScheduler:
    
    def __init__(self):
        self.queues = dict()
        self.activeList = deque([])
        self.isEmpty = True
        self.name = "DRR"
        
    def enqueueMessage(self, message):
        flowLabel = message.getFlowLabel()
        
        if (not self.queues.has_key(flowLabel)):
            self.queues[flowLabel] = Queue(message.hostId, message.flowId, 500)
            self.activeList.append(flowLabel)
            
        if (self.activeList.count(flowLabel) == 0):
            self.activeList.append(flowLabel)
        
        self.queues[flowLabel].messages.append(message)
        self.isEmpty = False
        
    def dequeueMessage(self):
        messagesToSend = []
        if (len(self.activeList) > 0):
            activeFlowLabel = self.activeList.popleft()
            queue = self.queues.get(activeFlowLabel)
            
            queue.deficit += queue.quantum

            while (queue.deficit > 0 and len(queue.messages) > 0):

                if (queue.messages[0].size <= queue.deficit):
                    queue.deficit -= queue.messages[0].size
                    messagesToSend.append(queue.messages.popleft())
                else:
                    break
            
            if (len(queue.messages) == 0):
                queue.deficit = 0
            else:
                self.activeList.append(activeFlowLabel)
        else:
            self.isEmpty = True
             
        return messagesToSend
            
                
class Queue:
    def __init__(self, hostId, flowId, quantum):
        self.messages=deque([])
        self.quantum=quantum
        self.hostId=hostId
        self.flowId=flowId
        self.deficit=0
        
class RrScheduler:
    
    def __init__(self):
        self.queues = dict()
        self.activeList = deque([])
        self.isEmpty = True
        self.name = "RR"
        
    def enqueueMessage(self, message):
        flowLabel = message.getFlowLabel()
        
        if (not self.queues.has_key(flowLabel)):
            self.queues[flowLabel] = deque([])
            
        if (self.activeList.count(flowLabel) == 0):
            self.activeList.append(flowLabel)
        
        self.queues[flowLabel].append(message)
        self.isEmpty = False
        
    def dequeueMessage(self):
        messagesToSend = []
        if (len(self.activeList) > 0):
            activeFlowLabel = self.activeList.popleft()

            if (len(self.queues.get(activeFlowLabel)) > 0):
                    messagesToSend.append(self.queues.get(activeFlowLabel).popleft())
                    self.activeList.append(activeFlowLabel)

        else:
            self.isEmpty = True
                    
        return messagesToSend